{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 发送电子邮件和短信笔记（第16章）\n",
    "### 1.1 发送电子邮件  \n",
    "简单邮件传输协议（SMTP）是用于发送电子邮件的协议。SMTP 规定电子邮件应该如何格式化、加密、在邮件服务器之间传递，以及在你点击发送后，计算机要处理的所有其他细节。。但是，你并不需要知道这些技术细节，因为Python 的smtplib 模块将它们简化成几个函数。SMTP只负责向别人发送电子邮件。\n",
    "SMTP发送邮件主要步骤如下：\n",
    "``` python\n",
    "import smtplib\n",
    "# 连接到SMTP 服务器\n",
    "smtpObj = smtplib.SMTP('smtp.example.com', 587)\n",
    "# 向SMTP 电子邮件服务器“打招呼”\n",
    "smtpObj.ehlo()\n",
    "(250, b'mx.example.com at your service, [216.172.148.131]\\nSIZE 35882577\\\n",
    "n8BITMIME\\nSTARTTLS\\nENHANCEDSTATUSCODES\\nCHUNKING')\n",
    "# 开始TLS 加密\n",
    "smtpObj.starttls()\n",
    "(220, b'2.0.0 Ready to start TLS')\n",
    "# 登陆账号\n",
    "smtpObj.login('bob@example.com', 'MY_SECRET_PASSWORD')\n",
    "(235, b'2.7.0 Accepted')\n",
    "# 发送邮件\n",
    "smtpObj.sendmail('bob@example.com', 'alice@example.com', 'Subject: So\n",
    "long.\\nDear Alice, so long and thanks for all the fish. Sincerely, Bob')\n",
    "# 断开连接\n",
    "smtpObj.quit()\n",
    "(221, b'2.0.0 closing connection ko10sm23097611pbd.52 - gsmtp')\n",
    "```\n",
    "\n",
    "|函数|用途|\n",
    "|-|-|\n",
    "|SMTP.connect(host='localhost',port=0)|链接 SMTP 服务器，host为SMTP 服务器常用域名，port为smtp端口|\n",
    "|smtpObj.ehlo()|判断是否链接服务器成功|\n",
    "|SMTP.login(user,password)|登陆需要认证的SMTP服务器，参数为用户名与密码|\n",
    "|SMTP.sendmail(from_addr,to_addrs,msg,mail_options=[],rcpt_options=[])|发送邮件，from_addr为发件人，to_addrs为收件人，msg为邮件内容|\n",
    "|SMTP.starttls(keyfile=None,certfile=None)|启用TLS安全传输模式|\n",
    "|SMTP.quit()|断开smtp服务器链接|\n",
    "\n",
    "提供商 SMTP 服务器常用域名见：\n",
    "https://blog.csdn.net/zdqdj1/article/details/48030023\n",
    "用Content-Type类型见:\n",
    "https://www.cnblogs.com/keyi/p/5833388.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在邮件主体中会常常包含 HTML、图像、声音以及附件格式等，MIME（Multipurpose Internet Mail Extensions，多用途互联网邮件扩展）作为一种新的扩展邮件格式很好地补充了这一点，更多MIME 知识见 https://docs.python.org/3/library/email.html。  Python 中常用的 MIME 实现类如下：\n",
    "\n",
    "|函数|用途|\n",
    "|-|-|\n",
    "|email.mime.base.MIMEBase（_maintype，_subtype)|MIME特定类的基类，_maintpe是Content-Type主要类型，_subtype是Content-Type次要类型 |\n",
    "|email.mime.multipart.MIMEMultipart（_subtype='mixed')|生成包含多个部分的 MIME 对象，_subtype取值 mixed、related、alternative|\n",
    "|email.mime.application.MIMEApplication(_ data, _ subtype='octet-stream', _ encoder=email.encoders.encode_base64| 添加应用，_ encoderw为编码格式，可使用email.encoders模块查看内置编码表|\n",
    "|email.mime.audio.MIMEAudio (_ audiodata, _ subtype=None, _ encoder)|创建音频数据，_audiodata原始二进制音频数据，_subtype音频类型，_encoder编码|\n",
    "|email.mime.image.MIMEImage(_ imagedata, _ subtype=None, _ encoder)|创建图像数据|\n",
    "|class email.mime.text.MIMEText(_ text, _ subtype='plain')|创建文本|"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 发送电子邮件具体实例\n",
    "#### 1.2.1 基础邮件发送\n",
    "基础邮件发送类似上面邮件发送步骤，只不过添加getpass模块，设置输入用户名和输入密码为暗文，保证安全性。调用email.mime模块，设置正文。具体代码如下\n",
    "```python\n",
    "import smtplib\n",
    "# 设置暗文\n",
    "import getpass\n",
    "# 设置邮件内容\n",
    "# 具体见https://docs.python.org/3/library/email.mime.html\n",
    "# 创建文本\n",
    "from email.mime.text import MIMEText\n",
    "# 设置邮件编码格式\n",
    "from email.header import Header\n",
    "\n",
    "# 连接到SMTP服务器\n",
    "# SMTP服务器名，服务端口是一个整数值，几乎总是587\n",
    "smtpObj = smtplib.SMTP('smtp-mail.outlook.com', 587)\n",
    "\n",
    "# starttls()让SMTP 连接处于TLS模式。返回值220告诉你，该服务器已准备就绪。\n",
    "print(smtpObj.starttls())\n",
    "\n",
    "# 提示输入用户名\n",
    "username = getpass.getpass(prompt=\"input username:\")\n",
    "# 提示输入密码\n",
    "password = getpass.getpass(prompt=\"input password:\")\n",
    "\n",
    "# 收件人\n",
    "recievername = ['abc@example.com', '123456@qq.com']\n",
    "# 返回值235表示认证成功\n",
    "loginStatus = smtpObj.login(username, password)\n",
    "print(loginStatus)\n",
    "\n",
    "\n",
    "# 设置内容，第二个参数表示文本\n",
    "msg = MIMEText('正文内容', 'plain', 'utf-8')\n",
    "# 设置标题\n",
    "msg['Subject'] = Header('标题', 'utf-8')\n",
    "\n",
    "try:\n",
    "    smtpObj.sendmail(username, recievername, msg.as_string())\n",
    "    print(\"邮件发送成功\")\n",
    "except:\n",
    "    print(\"Error: 无法发送邮件\")\n",
    "\n",
    "# 退出服务器\n",
    "smtpObj.quit()\n",
    "\n",
    "```\n",
    "---\n",
    "结果如下所示：\n",
    "![基础邮件发送](https://gitee.com/luminious/article_picture_warehouse/raw/master/Python-Study-Notes/email/16.1.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.2.2 发送HTML邮件\n",
    "如果我们要发送HTML邮件，而不是普通的纯文本文件怎么办？方法很简单，在构造MIMEText对象时，把HTML字符串传进去，再把第二个参数由plain变为html就可以了。\n",
    "\n",
    "``` python\n",
    "import smtplib\n",
    "# 设置暗文\n",
    "import getpass\n",
    "# 设置邮件编码格式\n",
    "from email.header import Header\n",
    "# 设置邮件内容\n",
    "# 具体见https://docs.python.org/3/library/email.mime.html\n",
    "from email.mime.text import MIMEText\n",
    "\n",
    "\n",
    "# 连接到SMTP服务器\n",
    "# SMTP服务器名，服务端口是一个整数值，几乎总是587\n",
    "smtpObj = smtplib.SMTP('smtp-mail.outlook.com', 587)\n",
    "\n",
    "# starttls()让SMTP 连接处于TLS模式。返回值220告诉你，该服务器已准备就绪。\n",
    "print(smtpObj.starttls())\n",
    "\n",
    "# 提示输入用户名\n",
    "username = getpass.getpass(prompt=\"input username:\")\n",
    "# 提示输入密码\n",
    "password = getpass.getpass(prompt=\"input password:\")\n",
    "\n",
    "# 收件人\n",
    "recievername = ['abc@example.com', '123456@qq.com']\n",
    "# 返回值235表示认证成功\n",
    "loginStatus = smtpObj.login(username, password)\n",
    "print(loginStatus)\n",
    "\n",
    "mail_msg = \"\"\"\n",
    "<p>邮件正文</p>\n",
    "<p><a href=\"https://blog.csdn.net/LuohenYJ\">我的博客</a></p>\n",
    "\"\"\"\n",
    "\n",
    "# 设置内容 html格式\n",
    "msg = MIMEText(mail_msg, 'html', 'utf-8')\n",
    "# 设置标题\n",
    "msg['Subject'] = Header('标题', 'utf-8')\n",
    "\n",
    "try:\n",
    "    smtpObj.sendmail(username, recievername, msg.as_string())\n",
    "    print(\"邮件发送成功\")\n",
    "except:\n",
    "    print(\"Error: 无法发送邮件\")\n",
    "\n",
    "# 退出服务器\n",
    "smtpObj.quit()\n",
    "```\n",
    "---\n",
    "结果如下所示：\n",
    "![基础邮件发送](https://gitee.com/luminious/article_picture_warehouse/raw/master/Python-Study-Notes/email/16.2.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.2.3 添加图像发送邮件\n",
    "```python\n",
    "import smtplib\n",
    "# 设置暗文\n",
    "import getpass\n",
    "\n",
    "# 设置邮件编码格式\n",
    "from email.header import Header\n",
    "\n",
    "# 多文件\n",
    "# 具体见 https://docs.python.org/3/library/email.mime.html\n",
    "# 详细说明见 https://blog.csdn.net/qdujunjie/article/details/8995334\n",
    "from email.mime.multipart import MIMEMultipart\n",
    "from email.mime.application import MIMEApplication\n",
    "# 设置图像\n",
    "from email.mime.image import MIMEImage\n",
    "# 设置邮件内容\n",
    "from email.mime.text import MIMEText\n",
    "import os\n",
    "import requests\n",
    "\n",
    "# 连接到SMTP服务器\n",
    "# SMTP服务器名，服务端口是一个整数值，几乎总是587\n",
    "smtpObj = smtplib.SMTP('smtp-mail.outlook.com', 587)\n",
    "\n",
    "# starttls()让SMTP 连接处于TLS模式。返回值220告诉你，该服务器已准备就绪。\n",
    "print(smtpObj.starttls())\n",
    "\n",
    "# 提示输入用户名\n",
    "username = getpass.getpass(prompt=\"input username:\")\n",
    "# 提示输入密码\n",
    "password = getpass.getpass(prompt=\"input password:\")\n",
    "\n",
    "\n",
    "# 收件人\n",
    "recievername = ['abc@example.com', '123456@qq.com']\n",
    "# 返回值235表示认证成功\n",
    "loginStatus = smtpObj.login(username, password)\n",
    "print(loginStatus)\n",
    "\n",
    "\n",
    "# 邮件的 HTML 文本中一般邮件服务商添加外链是无效的，但不是绝对的\n",
    "# 添加图像方式如下：\n",
    "\n",
    "\n",
    "# 正文内容\n",
    "# cid表示content-id\n",
    "mail_msg = \"\"\"\n",
    "<p>邮件正文</p>\n",
    "<p><a href=\"https://blog.csdn.net/LuohenYJ\">我的博客</a></p>\n",
    "<p>本地图片演示：</p>\n",
    "<p><img src=\"cid:imagelocal\"></p>\n",
    "<p>网络图片演示：</p>\n",
    "<p><img src=\"cid:imageurl\"></p>\n",
    "\"\"\"\n",
    "# 设置多内容\n",
    "msg = MIMEMultipart()\n",
    "# 添加正文\n",
    "msg.attach(MIMEText(mail_msg, 'html', 'utf-8'))\n",
    "\n",
    "# 指定本文图片\n",
    "fp = open('test.jpg', 'rb')\n",
    "msgImageLocal = MIMEImage(fp.read())\n",
    "fp.close()\n",
    "# 定义图片ID，在 HTML 文本中引用，和前面html对应\n",
    "msgImageLocal.add_header('Content-ID', '<imagelocal>')\n",
    "msg.attach(msgImageLocal)\n",
    "\n",
    "\n",
    "# 调用url,获取其内容图像\n",
    "url = \"https://img-blog.csdnimg.cn/20190308091244669.png\"\n",
    "page = requests.get(url)\n",
    "picture = page.content\n",
    "\n",
    "msgImageUrl = MIMEImage(picture)\n",
    "# 定义图片ID，在 HTML 文本中引用\n",
    "msgImageUrl.add_header('Content-ID', '<imageurl>')\n",
    "msg.attach(msgImageUrl)\n",
    "# 发送邮件\n",
    "\n",
    "try:\n",
    "    smtpObj.sendmail(username, recievername, msg.as_string())\n",
    "    print(\"邮件发送成功\")\n",
    "except:\n",
    "    print(\"Error: 无法发送邮件\")\n",
    "\n",
    "# 退出服务器\n",
    "smtpObj.quit()\n",
    "```\n",
    "---\n",
    "结果如下所示：\n",
    "![基础邮件发送](https://gitee.com/luminious/article_picture_warehouse/raw/master/Python-Study-Notes/email/16.3.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.2.4 添加附件发送邮件\n",
    "```python\n",
    "import smtplib\n",
    "# 设置暗文\n",
    "import getpass\n",
    "\n",
    "# 设置邮件编码格式\n",
    "from email.header import Header\n",
    "\n",
    "# 多文件\n",
    "# 具体见 https://docs.python.org/3/library/email.mime.html\n",
    "# 详细说明见 https://blog.csdn.net/qdujunjie/article/details/8995334\n",
    "from email.mime.multipart import MIMEMultipart\n",
    "from email.mime.application import MIMEApplication\n",
    "# 设置图像\n",
    "from email.mime.image import MIMEImage\n",
    "# 设置邮件内容\n",
    "from email.mime.text import MIMEText\n",
    "import os\n",
    "\n",
    "\n",
    "# 连接到SMTP服务器\n",
    "# SMTP服务器名，服务端口是一个整数值，几乎总是587\n",
    "smtpObj = smtplib.SMTP('smtp-mail.outlook.com', 587)\n",
    "\n",
    "# starttls()让SMTP 连接处于TLS模式。返回值220告诉你，该服务器已准备就绪。\n",
    "print(smtpObj.starttls())\n",
    "\n",
    "# 提示输入用户名\n",
    "username = getpass.getpass(prompt=\"input username:\")\n",
    "# 提示输入密码\n",
    "password = getpass.getpass(prompt=\"input password:\")\n",
    "\n",
    "\n",
    "# 收件人\n",
    "recievername = ['abc@example.com', '123456@qq.com']\n",
    "# 返回值235表示认证成功\n",
    "loginStatus = smtpObj.login(username, password)\n",
    "print(loginStatus)\n",
    "\n",
    "\n",
    "# 设置多内容\n",
    "msg = MIMEMultipart()\n",
    "\n",
    "\n",
    "#邮件正文内容\n",
    "# 第二个参数为文件子格式一般都是固定的\n",
    "msg.attach(MIMEText('正文', 'plain', 'utf-8'))\n",
    "# 标题\n",
    "msg['Subject'] = Header('标题', 'utf-8')\n",
    "\n",
    "\n",
    "# 具体用哪种MIME文件格式看附件格式\n",
    "# 按照上面提供的链接确定函数内容\n",
    "\n",
    "# 构造附件Text，传送当前目录下的 test.txt 文件\n",
    "# 参数分别是文件路径，文件子类型，编码格式\n",
    "attText = MIMEText(open('test.txt', 'rb').read(), 'base64', 'utf-8')\n",
    "# 设置http的Content-Type\n",
    "# 常用Content-Type类型见https://www.cnblogs.com/keyi/p/5833388.html\n",
    "# 如果Content-Type不知道就设置为application/octet-stream\n",
    "attText[\"Content-Type\"] = 'application/octet-stream'\n",
    "# filename邮件中文件显示名字\n",
    "attText[\"Content-Disposition\"] = 'attachment; filename=\"test.txt\"'\n",
    "msg.attach(attText)\n",
    "\n",
    "# 构造附件Image，传送当前目录下的test.jpg文件\n",
    "attImage = MIMEImage(open('test.jpg', 'rb').read(), 'jpg')\n",
    "attImage[\"Content-Type\"] = 'application/x-jpg'\n",
    "# filename邮件中文件显示名字\n",
    "attImage[\"Content-Disposition\"] = 'attachment; filename=\"test.jpg\"'\n",
    "msg.attach(attImage)\n",
    "\n",
    "# 构造附件Zip，传送当前目录下的test.zip文件\n",
    "# 防止文件不存在\n",
    "if os.path.exists('test.zip'):\n",
    "    attZip = MIMEApplication(open('test.zip', 'rb').read())\n",
    "    attZip[\"Content-Type\"] = 'application/zip'\n",
    "    # filename邮件中文件显示名字\n",
    "    attZip[\"Content-Disposition\"] = 'attachment; filename=\"test.zip\"'\n",
    "    msg.attach(attZip)\n",
    "\n",
    "\n",
    "# 发送邮件\n",
    "try:\n",
    "    smtpObj.sendmail(username, recievername, msg.as_string())\n",
    "    print(\"邮件发送成功\")\n",
    "except:\n",
    "    print(\"Error: 无法发送邮件\")\n",
    "\n",
    "# 退出服务器\n",
    "smtpObj.quit()\n",
    "```\n",
    "---\n",
    "结果如下所示：\n",
    "![基础邮件发送](https://gitee.com/luminious/article_picture_warehouse/raw/master/Python-Study-Notes/email/16.4.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 处理电子邮件  \n",
    "在Python 中，查找和获取电子邮件是一个多步骤的过程，需要第三方模块imapclient 和pyzmail。处理邮件主要步骤如下：\n",
    "``` python\n",
    ">>> import imapclient\n",
    "# 连接到IMAP 服务器\n",
    ">>> imapObj = imapclient.IMAPClient('imap.gmail.com', ssl=True)\n",
    "# 输入账号密码\n",
    ">>> imapObj.login('my_email_address@gmail.com', 'MY_SECRET_PASSWORD')\n",
    "'my_email_address@gmail.com Jane Doe authenticated (Success)'\n",
    "# 选择文件夹\n",
    ">>> imapObj.select_folder('INBOX', readonly=True)\n",
    "# 执行搜索\n",
    ">>> UIDs = imapObj.search(['SINCE'，'05-Jul-2014'])\n",
    ">>> UIDs0\n",
    "[40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039, 40040, 40041]\n",
    "# 取邮件\n",
    ">>> rawMessages = imapObj.fetch([40041], ['BODY[]', 'FLAGS'])\n",
    ">>> import pyzmail\n",
    "# 读邮件\n",
    ">>> message = pyzmail.PyzMessage.factory(rawMessages[40041]['BODY[]'])\n",
    ">>> message.get_subject()\n",
    "'Hello!'\n",
    "# 获得邮件发送者\n",
    ">>> message.get_addresses('from')\n",
    "[('Edward Snowden', 'esnowden@nsa.gov')]\n",
    ">>> message.get_addresses('to')\n",
    "[(Jane Doe', 'jdoe@example.com')]\n",
    ">>> message.get_addresses('cc')\n",
    "[]\n",
    ">>> message.get_addresses('bcc')\n",
    "[]\n",
    ">>> message.text_part != None\n",
    "True\n",
    ">>> message.text_part.get_payload().decode(message.text_part.charset)\n",
    "'Follow the money.\\r\\n\\r\\n-Ed\\r\\n'\n",
    ">>> message.html_part != None\n",
    "True\n",
    ">>> message.html_part.get_payload().decode(message.html_part.charset)\n",
    "'<div dir=\"ltr\"><div>So long, and thanks for all the fish!<br><br></div>-\n",
    "Al<br></div>\\r\\n'\n",
    "# 登出\n",
    ">>> imapObj.logout()\n",
    "```\n",
    "对于常用的时间模块具体。python设置时间主要time模块和datetime模块。通过import time和import datetime调用time模块和datetime模块。常用函数如下：\n",
    "\n",
    "|函数|用途|备注|\n",
    "|-|-|-|\n",
    "|time.time()函数|返回自Unix纪元时(协调世界时UTC)的秒数|Unix 纪元时间：1970 年1 月1 日0 点，即协调世界时|\n",
    "|time.sleep(n)|让程序暂停一下n秒|按Ctrl-C 不会中断time.sleep()调用|\n",
    "|datetime.datetime.now()|返回当前的日期和时间|包含当前时刻的年、月、日、时、分、秒和微秒|\n",
    "|datetime.datetime(2015, 10, 21, 16, 29, 0)|得到特定时刻的datetime 对象||\n",
    "|datetime.datetime.fromtimestamp(time)|将Unix 纪元时间戳转换为datetime对象||\n",
    "|delta = datetime.timedelta(days=11, hours=10, minutes=9, seconds=8)|创建timedelta 数据类型表示一段时间||\n",
    "|delta.days/delta.seconds/delta.microseconds|获得timedelta对象拥有的总时间以天、秒、微秒来表示||\n",
    "|delta.total_seconds()|返回只以秒表示的delta时间||\n",
    "|strftime()|将datetime 对象转换为字符串||\n",
    "|strptime()|将字符串转换成 datetime 对象||"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3 参考\n",
    "+ https://docs.python.org/3/library/email.mime.html\n",
    "+ https://www.runoob.com/python/python-email.html\n",
    "+ https://www.cnblogs.com/zhangxinqi/p/9113859.html\n",
    "+ https://www.liaoxuefeng.com/wiki/1016959663602400/1017790702398272\n",
    "+ https://tools.ietf.org/html/rfc3501.html#section-6.4.4"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
